Sophisticated microprocessor development systems use a technique called "emulation" which is accomplished by an "emulator." An emulator contains a substitute microprocessor and associated electronics that is used in place of the ultimately intended microprocessor. Typically, the target system's microprocessor is removed, and in its place a cable having a suitable probe inserted into the socket. The cable and the probe connect the target system to the emulation hardware. Within the emulator is an emulation microprocessor that is a high speed version of what is otherwise of the same type as the target processor, although once in a while the emulation processor will be a slightly modified version of the commercially available customer version (certain additional internal signals might be brought out to better indicate internal activities). The emulation processor is embedded in suitable control circuitry to allow it to be started, stopped, and to allow its internal registers to be inspected or changed. It is also common for emulators to provide a means to select whether the emulator processor executes the target system's code from memory actually located in the target system, or instead from memory located within the emulator itself.
A modern emulator for a powerful state of the art microprocessor is a complicated affair, and the facilities required to investigate or alter the contents of the target system's executable code, set breakpoints, do single stepping, report on and change internal register contents, etc., generally require the assistance of a mechanism dedicated to just such purposes. That mechanism is called a monitor, and it is typically a miniature special purpose operating system assisted by some dedicated hardware. In the most powerful architectures the emulation mechanism is accompanied by an internal logic state analyzer, and perhaps even by a timing analyzer. The overall control of these separate but integrated functions exceeds the scope of the monitor, and is frequently accomplished by yet another microprocessor which we shall be content to call the "controller." The controller gets its commands from, and reports status and data to, a conventional terminal, or perhaps yet another computer. We shall not distinguish here between the two; whichever one it is is simply the "user," "host" or the "operator."
Microprocessor development systems incorporating emulation as described above became available shortly after microprocessors appeared on the scene. Initially, the typical system under development used only a single microprocessor so there was little need for an emulator to be congenial and cooperate with another instance of itself. But the microprocessor is now ubiquitous, and even the top of the line models are small and inexpensive in comparison to what was required to accomplish the same functions only a decade ago. As a result, microprocessors are showing up in parallel processing architectures, as well as in many applications outside of traditional "computing." They are being used in embedded systems that control voltmeters, digital oscilloscopes, logic analyzers, emulators, and in various forms of very large scale systems that incorporate a variety of machinery and equipment, both manned and unmanned, e.g., high performance aircraft and robotic mechanisms. Such a large scale system may incorporate a substantial number of (say, a dozen or more) microprocessors to control its major and minor subsystems. The subsystems need to coordinate their activities; that is, the various microprocessors in the finished system need to communicate commands, status and data. Structured design and disciplined programming notwithstanding, this is positively fertile ground for bugs. A microprocessor development system that is intended for use in a single processor environment lacks the tools to efficiently assist in tracking down bugs whose mechanisms involve interaction between two or more subsystems and their controlling processors. What good does it do, for example, to bring one emulation processor to a halt immediately at the behest of its associated state analyzer (or as a result of some other resource available locally within that emulation/analyzer combination) when it may take several hundred or a thousand instructions cycles of the other emulators to get them stopped. The longer they run the less clear is the picture of the conditions surrounding the event of interest.
There are some prior art emulators that can be stopped only by detection of events internal to the emulator, or by receipt of an operator issued "stop" command, as from a keyboard. That is, these emulators do not contemplate at all the notion of stopping in response to a signal originating with another emulator or separate analysis instrument.
The problem is that conventional emulator architecture is not agile; it takes it a long time (measured in processor instruction cycles) to get the emulator going, and a long time to get it stopped. The emulator and its controller represent a substantial software engine in its own right, and its attention must first be gained before any starting and stopping can be done. Next, there can be a substantial amount of emulation overhead that necessarily accompanies starting and stopping, no matter how quickly the controller responds. Furthermore, prior art emulator-to-emulator communications mechanisms have operated at a relatively high level, i.e., mailboxes and semaphores. No wonder by the time all the emulation processors get stopped in response to some event of interest, the user is extremely lucky to be able to glean any useful knowledge about what was really going on at the time the event occurred. What is needed is a way to politely but immediately bring all the processors to a halt, so that the tracks of the culprit are not covered by subsequent operation.
A similar but related problem attends the task of getting all the processors in a target system started at the same time with variable selected internal conditions for the various subsystems. One way this can be accomplished is to get all of the emulators running (which may require visiting each emulator) while holding the all of the target processors' RESET lines in the active state, and then simultaneously releasing those RESET lines. This does get all of the subsystems running from known initial conditions. Unfortunately, it may well not be the desired initial conditions, and it may require a great deal of subsequent work to bring those desired conditions into being.
In several prior art systems, getting the various emulators to begin a test with each subsystem in a particular location or state (the initial conditions for some test) has required the operator or his assistants to physically walk over to each emulator and manipulate its keyboard to get the emulator going and then do whatever is necessary to establish the desired initial conditions within the subsystem.
Not only does this lack of emulator agility make process synchronism difficult, but it also makes it nearly impossible to meaningfully resume system activity after an interruption (a pause, continue, pause, continue, . . . type operation). What is needed is a way of getting all the processors in the target system going either from wherever they are or from some selected starting points, with a minimum of variation of the points in time when each subsystem resumes execution of its used code.
Compounding this situation is a trend in the architecture of high performance multi-processor embedded systems: tight coupling. The designers of such systems are well aware that the use of formal I/O channels for semaphores and mail boxes, all handled by firmware, can be time consuming bottle necks. Other designs are sought, such as ones that share common data in multi-port memories. With these types of designs the awkwardness of the conventional emulator in dealing with other emulators further compounds the task of debugging the system.